// Implementation of item view container for FLTK
// Copyright 2005 (C) Ralph Thomas

#include <view/item.h>
#include <view/fltk/common.h>
#include <fltk/Group.h>
#include <fltk/Color.h>
#include <fltk/Style.h>
#include <fltk/events.h>
#include <fltk/draw.h>
#include <fltk/damage.h>

class itemWidget : public fltk::Group {
	adobe::name_t	_name;	///< The name of the cell we're bound to.
	adobe::value_t	_val;	///< The value we change color for.
	adobe::factory_token_t _token;	///< The token to take the sheet and notifer from.
	bool		_selected;///< Are we selected now or not?
	adobe::name_t	_action;///< The action to perform when we're double clicked.
	//
	// void _update( const adobe::value_t& val )
	//
	/// If the value is the same as the one that we have stored then we
	/// change our background color to selected, otherwise we change our
	/// background color to normal (buttonface).
	///
	/// \param	val	the value to compare with.
	//
	void _update( const adobe::value_t& val ) {
		//
		// Simple optimization to make sure that we only redraw when
		// our background color actually changed.
		//
		if( (val == _val) && !_selected ) {
			_selected = true;
			color( selection_color() );
			labelcolor( fltk::WHITE );
			textcolor( fltk::WHITE );
			redraw( fltk::DAMAGE_ALL | fltk::DAMAGE_EXPOSE );
			fltk::focus( this );
		} else if( _selected ) {
			_selected = false;
			color( fltk::WHITE );
			labelcolor( fltk::BLACK );
			textcolor( fltk::BLACK );
			redraw( fltk::DAMAGE_ALL | fltk::DAMAGE_EXPOSE );
		}
	}
  public:
	itemWidget( const adobe::dictionary_t& params, const adobe::factory_token_t& token )
	: fltk::Group( 0, 0, 10, 10 ), _token( token ), _selected( false ) {
		adobe::name_t style( adobe::static_name_t( "rounded" ) );
		params.get( adobe::static_name_t( "bind" ), _name );
		params.get( adobe::static_name_t( "on_value" ), _val );
		params.get( adobe::static_name_t( "action" ), _action );
		params.get( adobe::static_name_t( "style" ), style );
		color( fltk::WHITE );
		//
		// Bind to the sheet to get updates on our cell.
		//
		if( _name ) {
		       	token.assemblage_m.hold_connection( _token.sheet_m.monitor_value(
				_name,
				boost::bind( &itemWidget::_update, this, _1 ) ) );
			//
			// If we are created on the fly, then we won't get the
			// message saying that the cell got it's initial value.
			//
			_token.sheet_m.touch( &_name, (&_name)+1 );
		}
		//
		// Set our color and box style (so that we get a box drawn, and have
		// a good default color.
		//
		if( style == adobe::static_name_t( "rounded" ) )
			box( fltk::RFLAT_BOX );
		else
			box( fltk::FLAT_BOX );
	}
	virtual ~itemWidget() {
		//
		// Don't lose the focus when we die.
		//
		if( fltk::focus() == this ) fltk::focus( parent() );
	}
	virtual int handle( int code ) {
		int rval = fltk::Group::handle( code );
		if( (code == fltk::PUSH) && (rval == 0) ) {
			if( fltk::event_clicks() == 1 ) {
				//
				// Perform our action.
				// XXX: The way we do this (notifiers, etc) is something
				// XXX: I still need to figure out for models. The current
				// XXX: way isn't very direct, and isn't easily customizable
				// XXX: to a bunch of items in a list. This way is kind of
				// XXX: forced.
				//
				if( _action ) _token.notifier_m( _action, _val );
				return 1;
			}
			//
			// There was a push event, and it looks like none of the
			// children handled it.
			//
			if( _name ) { _token.sheet_m.set( _name, _val ); _token.sheet_m.update(); }
			return 1;
		}
		if( code == fltk::HIDE ) {
			//
			// If we are hidden with focus then focus our parent.
			//
			if( fltk::focus() == this ) fltk::focus( parent() );
		}
		//
		// XXX: Also... need to handle doubleclicks.
		//
		return rval;
	}
	virtual void layout() {
		fltk::Widget::layout();
	}
};

namespace view {
	adobe::widget_node_t item_factory(
		const adobe::dictionary_t&	parameters,
		const adobe::widget_node_t&	parent,
		const adobe::factory_token_t&	token ) {
		//
		// Create an itemWidget and set it to delete when the assemblage
		// is destroyed.
		//
		fltk::Widget* iw( new itemWidget( parameters, token ) );
		token.assemblage_m.delete_on_destruction( iw );
		//
		// Insert this widget into our display and parent.
		//
		adobe::display_t::position_t display_token( adobe::insert(
			adobe::get_main_display(), parent.display_token_m,
			iw ) );
		adobe::display_t::position_t overlay_token(
			token.overlay_m.insert( parent.overlay_token_m ) );
		//
		// Bind calculate and place signals for Eve.
		//
		adobe::extents_t min_size;
		min_size.width() = 100;
		min_size.height() = 30;

		adobe::eve_t::calculate_data_t cdata;
		cdata.placement_m = adobe::eve_t::place_row;
		cdata.horizontal().margin_m.first = 10;
		cdata.horizontal().margin_m.second = 10;
		cdata.vertical().margin_m.first = 4;
		cdata.vertical().margin_m.second = 4;

		eve_client::place_func_t placer( boost::bind( &place, iw, _1, _2 ) );
		adobe::eve_t::signal_suite_t signals;
		signals.eve_calculate_proc_m = boost::bind(
			&eve_client::utility_widget_calculate,
			min_size, _1 );
		signals.eve_place_proc_m = boost::bind(
			&eve_client::utility_widget_place,
			placer, _1, _2, boost::ref( token.overlay_m ),
			overlay_token );
		//
		// Register this item with Eve.
		//
		adobe::eve_t::iterator eve_token(
			token.layout_m.add_view_element( parent.eve_token_m,
				adobe::eve_t::insert_element_t(
					cdata,
					true,
					parameters,
					signals )
				)
			);
		//
		// Return the item.
		//
		return adobe::widget_node_t( parent.size_m, eve_token, display_token,
			overlay_token );
	}
};

